package godot.intellij.plugin.linemarker

import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.codeInsight.daemon.LineMarkerProvider
import com.intellij.openapi.editor.markup.GutterIconRenderer
import com.intellij.openapi.ide.CopyPasteManager
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.util.IconLoader
import com.intellij.psi.JavaTokenType
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiField
import com.intellij.psi.PsiMethod
import com.intellij.psi.impl.source.tree.LeafPsiElement
import godot.common.extensions.convertToSnakeCase
import godot.intellij.plugin.GodotPluginBundle
import godot.intellij.plugin.data.model.REGISTER_CLASS_ANNOTATION
import godot.intellij.plugin.data.model.REGISTER_FUNCTION_ANNOTATION
import godot.intellij.plugin.data.model.REGISTER_PROPERTY_ANNOTATION
import godot.intellij.plugin.data.model.REGISTER_SIGNAL_ANNOTATION
import godot.intellij.plugin.extension.asClassId
import godot.intellij.plugin.extension.getRegisteredClassName
import godot.intellij.plugin.extension.isAbstract
import godot.intellij.plugin.extension.isInGodotRoot
import org.jetbrains.kotlin.idea.refactoring.isAbstract
import org.jetbrains.kotlin.idea.util.findAnnotation
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtProperty
import java.awt.datatransfer.StringSelection
import javax.swing.Icon

private data class LineMarkerData(
    val identifier: PsiElement,
    val convertedName: String,
    val gutterIcon: Icon
)

class RegisteredNameLineMarker : LineMarkerProvider {
    override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? {
        if (DumbService.getInstance(element.project).isDumb) return null
        if (!element.isInGodotRoot()) return null

        if (element !is LeafPsiElement || (element.elementType != KtTokens.IDENTIFIER && element.elementType != JavaTokenType.IDENTIFIER)) {
            return null
        }

        val lineMarkerData = when(val parent = element.parent) {
            is KtClass -> if (parent.findAnnotation(asClassId(REGISTER_CLASS_ANNOTATION)) != null) {
                val name = if (parent.isAbstract()) {
                    GodotPluginBundle.message(
                        "linemarker.registeredName.notRegisteredBecauseIsAbstract.text"
                    )
                } else {
                    parent.getRegisteredClassName()?.second ?: "<unknown>"
                }
                LineMarkerData(
                    identifier = parent.nameIdentifier ?: parent.navigationElement,
                    convertedName = name,
                    gutterIcon = IconLoader.getIcon("/linemarkerIcons/icon_script.svg", this::class.java)
                )
            } else null
            is PsiClass -> if (parent.getAnnotation(REGISTER_CLASS_ANNOTATION) != null) {
                val name = if (parent.isAbstract) {
                    GodotPluginBundle.message(
                        "linemarker.registeredName.notRegisteredBecauseIsAbstract.text"
                    )
                } else {
                    parent.getRegisteredClassName()?.second ?: "<unknown>"
                }

                LineMarkerData(
                    identifier = parent.nameIdentifier ?: parent.navigationElement,
                    convertedName = name,
                    gutterIcon = IconLoader.getIcon("/linemarkerIcons/icon_script.svg", this::class.java)
                )
            } else null
            is PsiMethod -> if (parent.getAnnotation(REGISTER_FUNCTION_ANNOTATION) != null) {
                LineMarkerData(
                    identifier = parent.nameIdentifier ?: parent.navigationElement,
                    convertedName = parent.name.convertToSnakeCase(),
                    gutterIcon = IconLoader.getIcon("/linemarkerIcons/icon_member_method.svg", this::class.java)
                )
            } else null
            is PsiField -> when {
                parent.getAnnotation(REGISTER_PROPERTY_ANNOTATION) != null -> LineMarkerData(
                    identifier = parent.nameIdentifier,
                    convertedName = parent.name.convertToSnakeCase(),
                    gutterIcon = IconLoader.getIcon("/linemarkerIcons/icon_member_property.svg", this::class.java)
                )
                parent.getAnnotation(REGISTER_SIGNAL_ANNOTATION) != null -> LineMarkerData(
                    identifier = parent.nameIdentifier,
                    convertedName = parent.name.convertToSnakeCase(),
                    gutterIcon = IconLoader.getIcon("/linemarkerIcons/icon_member_signal.svg", this::class.java)
                )
                else -> null
            }
            is KtProperty -> when {
                parent.findAnnotation(asClassId(REGISTER_PROPERTY_ANNOTATION)) != null -> parent.name?.let { name ->
                    LineMarkerData(
                        identifier = parent.nameIdentifier ?: parent.navigationElement,
                        convertedName = name.convertToSnakeCase(),
                        gutterIcon = IconLoader.getIcon("/linemarkerIcons/icon_member_property.svg", this::class.java)
                    )
                }
                parent.findAnnotation(asClassId(REGISTER_SIGNAL_ANNOTATION)) != null -> parent.name?.let { name ->
                    LineMarkerData(
                        identifier = parent.nameIdentifier ?: parent.navigationElement,
                        convertedName = name.convertToSnakeCase(),
                        gutterIcon = IconLoader.getIcon("/linemarkerIcons/icon_member_signal.svg", this::class.java)
                    )
                }
                else -> null
            }
            is KtFunction -> when {
                parent.findAnnotation(asClassId(REGISTER_FUNCTION_ANNOTATION)) != null -> parent.name?.let { name ->
                    LineMarkerData(
                        identifier = parent.nameIdentifier ?: parent.navigationElement,
                        convertedName = name.convertToSnakeCase(),
                        gutterIcon = IconLoader.getIcon("/linemarkerIcons/icon_member_method.svg", this::class.java)
                    )
                }
                else -> null
            }
            else -> null
        }

        return if (lineMarkerData != null) {
            val tooltipText = GodotPluginBundle.message("linemarker.registeredName.tooltip", lineMarkerData.convertedName)

            LineMarkerInfo(
                lineMarkerData.identifier,
                lineMarkerData.identifier.textRange,
                lineMarkerData.gutterIcon,
                { tooltipText },
                { _, _ ->
                    CopyPasteManager.getInstance().setContents(StringSelection(lineMarkerData.convertedName))
                },
                GutterIconRenderer.Alignment.LEFT,
                { "registered name line marker" }
            )
        } else null
    }
}
